package com.hs.jhzd.config.mq;

import java.util.LinkedHashSet;
import java.util.Set;

import org.springframework.amqp.core.AcknowledgeMode;
import org.springframework.amqp.rabbit.connection.CachingConnectionFactory.CacheMode;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
import org.springframework.util.StringUtils;

import com.hs.jhzd.config.MqCondition;

/**
 * The Class RabbitProperties.
 */
@Configuration
@Conditional(MqCondition.class)
public class RabbitProperties {

  /**
   * RabbitMQ host.
   */
  @Value("${mq.host}")
  private String host;

  /**
   * RabbitMQ port.
   */
  @Value("${mq.port}")
  private int port;

  /**
   * Login user to authenticate to the broker.
   */
  @Value("${mq.username}")
  private String username;

  /**
   * Login to authenticate against the broker.
   */
  @Value("${mq.password}")
  private String password;

  /** The virtual host. */
  @Value("${mq.vhosts}")
  private String virtualHost;

  /**
   * SSL configuration.
   */
  private final Ssl ssl = new Ssl();

  /**
   * Virtual host to use when connecting to the broker.
   */
  // private String virtualHost = "/";

  /**
   * Comma-separated list of addresses to which the client should connect.
   */
  private String addresses;

  /**
   * Requested heartbeat timeout, in seconds; zero for none.
   */
  private Integer requestedHeartbeat;

  /**
   * Enable publisher confirms.
   */
  private boolean publisherConfirms;

  /**
   * Enable publisher returns.
   */
  private boolean publisherReturns;

  /**
   * Connection timeout, in milliseconds; zero for infinite.
   */
  private Integer connectionTimeout = 6000;

  /**
   * Cache configuration.
   */
  private final Cache cache = new Cache();

  /**
   * Listener container configuration.
   */
  private final Listener listener = new Listener();

  /** The template. */
  private final Template template = new Template();

  /**
   * 获取host.
   * 
   * @return host
   */
  public String getHost() {
    if (this.addresses == null) {
      return this.host;
    }
    String[] hosts = StringUtils.delimitedListToStringArray(this.addresses, ":");
    if (hosts.length == 2) {
      return hosts[0];
    }
    return null;
  }

  /**
   * Sets the host.
   *
   * @param host the new host
   */
  public void setHost(String host) {
    this.host = host;
  }

  /**
   * 获取port.
   * 
   * @return port
   */
  public int getPort() {
    if (this.addresses == null) {
      return this.port;
    }
    String[] hosts = StringUtils.delimitedListToStringArray(this.addresses, ":");
    if (hosts.length >= 2) {
      return Integer.valueOf(StringUtils.commaDelimitedListToStringArray(hosts[1])[0]);
    }
    return this.port;
  }

  /**
   * Sets the addresses.
   *
   * @param addresses the new addresses
   */
  public void setAddresses(String addresses) {
    this.addresses = parseAddresses(addresses);
  }

  /**
   * Gets the addresses.
   *
   * @return the addresses
   */
  public String getAddresses() {
    return (this.addresses == null ? this.host + ":" + this.port : this.addresses);
  }

  /**
   * Parses the addresses.
   *
   * @param addresses the addresses
   * @return the string
   */
  private String parseAddresses(String addresses) {
    Set<String> result = new LinkedHashSet<>();
    for (String address : StringUtils.commaDelimitedListToStringArray(addresses)) {
      address = address.trim();
      if (address.startsWith("amqp://")) {
        address = address.substring("amqp://".length());
      }
      if (address.contains("@")) {
        String[] split = StringUtils.split(address, "@");
        String creds = split[0];
        address = split[1];
        split = StringUtils.split(creds, ":");
        this.username = split[0];
        if (split.length > 0) {
          this.password = split[1];
        }
      }
      int index = address.indexOf("/");
      if (index >= 0 && index < address.length()) {
        setVirtualHost(address.substring(index + 1));
        address = address.substring(0, index);
      }
      if (!address.contains(":")) {
        address = address + ":" + this.port;
      }
      result.add(address);
    }
    return (result.isEmpty() ? null : StringUtils.collectionToCommaDelimitedString(result));
  }

  /**
   * Sets the port.
   *
   * @param port the new port
   */
  public void setPort(int port) {
    this.port = port;
  }

  /**
   * Gets the username.
   *
   * @return the username
   */
  public String getUsername() {
    return this.username;
  }

  /**
   * Sets the username.
   *
   * @param username the new username
   */
  public void setUsername(String username) {
    this.username = username;
  }

  /**
   * Gets the password.
   *
   * @return the password
   */
  public String getPassword() {
    return this.password;
  }

  /**
   * Sets the password.
   *
   * @param password the new password
   */
  public void setPassword(String password) {
    this.password = password;
  }

  /**
   * Gets the ssl.
   *
   * @return the ssl
   */
  public Ssl getSsl() {
    return this.ssl;
  }

  /**
   * Gets the virtual host.
   *
   * @return the virtual host
   */
  public String getVirtualHost() {
    return this.virtualHost;
  }

  /**
   * Sets the virtual host.
   *
   * @param virtualHost the new virtual host
   */
  public void setVirtualHost(String virtualHost) {
    this.virtualHost = ("".equals(virtualHost) ? "/" : virtualHost);
  }

  /**
   * Gets the requested heartbeat.
   *
   * @return the requested heartbeat
   */
  public Integer getRequestedHeartbeat() {
    return this.requestedHeartbeat;
  }

  /**
   * Sets the requested heartbeat.
   *
   * @param requestedHeartbeat the new requested heartbeat
   */
  public void setRequestedHeartbeat(Integer requestedHeartbeat) {
    this.requestedHeartbeat = requestedHeartbeat;
  }

  /**
   * Checks if is publisher confirms.
   *
   * @return true, if is publisher confirms
   */
  public boolean isPublisherConfirms() {
    return this.publisherConfirms;
  }

  /**
   * Sets the publisher confirms.
   *
   * @param publisherConfirms the new publisher confirms
   */
  public void setPublisherConfirms(boolean publisherConfirms) {
    this.publisherConfirms = publisherConfirms;
  }

  /**
   * Checks if is publisher returns.
   *
   * @return true, if is publisher returns
   */
  public boolean isPublisherReturns() {
    return this.publisherReturns;
  }

  /**
   * Sets the publisher returns.
   *
   * @param publisherReturns the new publisher returns
   */
  public void setPublisherReturns(boolean publisherReturns) {
    this.publisherReturns = publisherReturns;
  }

  /**
   * Gets the connection timeout.
   *
   * @return the connection timeout
   */
  public Integer getConnectionTimeout() {
    return this.connectionTimeout;
  }

  /**
   * Sets the connection timeout.
   *
   * @param connectionTimeout the new connection timeout
   */
  public void setConnectionTimeout(Integer connectionTimeout) {
    this.connectionTimeout = connectionTimeout;
  }

  /**
   * Gets the cache.
   *
   * @return the cache
   */
  public Cache getCache() {
    return this.cache;
  }

  /**
   * Gets the listener.
   *
   * @return the listener
   */
  public Listener getListener() {
    return this.listener;
  }

  /**
   * Gets the template.
   *
   * @return the template
   */
  public Template getTemplate() {
    return this.template;
  }

  /**
   * The Class Ssl.
   */
  public static class Ssl {

    /**
     * Enable SSL support.
     */
    private boolean enabled;

    /**
     * Path to the key store that holds the SSL certificate.
     */
    private String keyStore;

    /**
     * Password used to access the key store.
     */
    private String keyStorePassword;

    /**
     * Trust store that holds SSL certificates.
     */
    private String trustStore;

    /**
     * Password used to access the trust store.
     */
    private String trustStorePassword;

    /**
     * SSL algorithm to use (e.g. TLSv1.1). Default is set automatically by the rabbit client
     * library.
     */
    private String algorithm;

    /**
     * Checks if is enabled.
     *
     * @return true, if is enabled
     */
    public boolean isEnabled() {
      return this.enabled;
    }

    /**
     * Sets the enabled.
     *
     * @param enabled the new enabled
     */
    public void setEnabled(boolean enabled) {
      this.enabled = enabled;
    }

    /**
     * Gets the key store.
     *
     * @return the key store
     */
    public String getKeyStore() {
      return this.keyStore;
    }

    /**
     * Sets the key store.
     *
     * @param keyStore the new key store
     */
    public void setKeyStore(String keyStore) {
      this.keyStore = keyStore;
    }

    /**
     * Gets the key store password.
     *
     * @return the key store password
     */
    public String getKeyStorePassword() {
      return this.keyStorePassword;
    }

    /**
     * Sets the key store password.
     *
     * @param keyStorePassword the new key store password
     */
    public void setKeyStorePassword(String keyStorePassword) {
      this.keyStorePassword = keyStorePassword;
    }

    /**
     * Gets the trust store.
     *
     * @return the trust store
     */
    public String getTrustStore() {
      return this.trustStore;
    }

    /**
     * Sets the trust store.
     *
     * @param trustStore the new trust store
     */
    public void setTrustStore(String trustStore) {
      this.trustStore = trustStore;
    }

    /**
     * Gets the trust store password.
     *
     * @return the trust store password
     */
    public String getTrustStorePassword() {
      return this.trustStorePassword;
    }

    /**
     * Sets the trust store password.
     *
     * @param trustStorePassword the new trust store password
     */
    public void setTrustStorePassword(String trustStorePassword) {
      this.trustStorePassword = trustStorePassword;
    }

    /**
     * Gets the algorithm.
     *
     * @return the algorithm
     */
    public String getAlgorithm() {
      return this.algorithm;
    }

    /**
     * Sets the algorithm.
     *
     * @param sslAlgorithm the new algorithm
     */
    public void setAlgorithm(String sslAlgorithm) {
      this.algorithm = sslAlgorithm;
    }

  }

  /**
   * The Class Cache.
   */
  public static class Cache {

    /** The channel. */
    private final Channel channel = new Channel();

    /** The connection. */
    private final Connection connection = new Connection();

    /**
     * Gets the channel.
     *
     * @return the channel
     */
    public Channel getChannel() {
      return this.channel;
    }

    /**
     * Gets the connection.
     *
     * @return the connection
     */
    public Connection getConnection() {
      return this.connection;
    }

    /**
     * The Class Channel.
     */
    public static class Channel {

      /**
       * Number of channels to retain in the cache. When "check-timeout" > 0, max channels per
       * connection.
       */
      private Integer size;

      /**
       * Number of milliseconds to wait to obtain a channel if the cache size has been reached. If
       * 0, always create a new channel.
       */
      private Long checkoutTimeout;

      /**
       * Gets the size.
       *
       * @return the size
       */
      public Integer getSize() {
        return this.size;
      }

      /**
       * Sets the size.
       *
       * @param size the new size
       */
      public void setSize(Integer size) {
        this.size = size;
      }

      /**
       * Gets the checkout timeout.
       *
       * @return the checkout timeout
       */
      public Long getCheckoutTimeout() {
        return this.checkoutTimeout;
      }

      /**
       * Sets the checkout timeout.
       *
       * @param checkoutTimeout the new checkout timeout
       */
      public void setCheckoutTimeout(Long checkoutTimeout) {
        this.checkoutTimeout = checkoutTimeout;
      }

    }

    /**
     * The Class Connection.
     */
    public static class Connection {

      /**
       * Connection factory cache mode.
       */
      private CacheMode mode = CacheMode.CHANNEL;

      /**
       * Number of connections to cache. Only applies when mode is CONNECTION.
       */
      private Integer size;

      /**
       * Gets the mode.
       *
       * @return the mode
       */
      public CacheMode getMode() {
        return this.mode;
      }

      /**
       * Sets the mode.
       *
       * @param mode the new mode
       */
      public void setMode(CacheMode mode) {
        this.mode = mode;
      }

      /**
       * Gets the size.
       *
       * @return the size
       */
      public Integer getSize() {
        return this.size;
      }

      /**
       * Sets the size.
       *
       * @param size the new size
       */
      public void setSize(Integer size) {
        this.size = size;
      }

    }

  }

  /**
   * The Class Listener.
   */
  public static class Listener {

    /**
     * Start the container automatically on startup.
     */
    private boolean autoStartup = true;

    /**
     * Acknowledge mode of container.
     */
    private AcknowledgeMode acknowledgeMode;

    /**
     * Minimum number of consumers.
     */
    private Integer concurrency;

    /**
     * Maximum number of consumers.
     */
    private Integer maxConcurrency;

    /**
     * Number of messages to be handled in a single request. It should be greater than or equal to
     * the transaction size (if used).
     */
    private Integer prefetch;

    /**
     * Number of messages to be processed in a transaction. For best results it should be less than
     * or equal to the prefetch count.
     */
    private Integer transactionSize;

    /**
     * Whether rejected deliveries are requeued by default; default true.
     */
    private Boolean defaultRequeueRejected;

    /**
     * Optional properties for a retry interceptor.
     */
    // @NestedConfigurationProperty
    private final ListenerRetry retry = new ListenerRetry();

    /**
     * Checks if is auto startup.
     *
     * @return true, if is auto startup
     */
    public boolean isAutoStartup() {
      return this.autoStartup;
    }

    /**
     * Sets the auto startup.
     *
     * @param autoStartup the new auto startup
     */
    public void setAutoStartup(boolean autoStartup) {
      this.autoStartup = autoStartup;
    }

    /**
     * Gets the acknowledge mode.
     *
     * @return the acknowledge mode
     */
    public AcknowledgeMode getAcknowledgeMode() {
      return this.acknowledgeMode;
    }

    /**
     * Sets the acknowledge mode.
     *
     * @param acknowledgeMode the new acknowledge mode
     */
    public void setAcknowledgeMode(AcknowledgeMode acknowledgeMode) {
      this.acknowledgeMode = acknowledgeMode;
    }

    /**
     * Gets the concurrency.
     *
     * @return the concurrency
     */
    public Integer getConcurrency() {
      return this.concurrency;
    }

    /**
     * Sets the concurrency.
     *
     * @param concurrency the new concurrency
     */
    public void setConcurrency(Integer concurrency) {
      this.concurrency = concurrency;
    }

    /**
     * Gets the max concurrency.
     *
     * @return the max concurrency
     */
    public Integer getMaxConcurrency() {
      return this.maxConcurrency;
    }

    /**
     * Sets the max concurrency.
     *
     * @param maxConcurrency the new max concurrency
     */
    public void setMaxConcurrency(Integer maxConcurrency) {
      this.maxConcurrency = maxConcurrency;
    }

    /**
     * Gets the prefetch.
     *
     * @return the prefetch
     */
    public Integer getPrefetch() {
      return this.prefetch;
    }

    /**
     * Sets the prefetch.
     *
     * @param prefetch the new prefetch
     */
    public void setPrefetch(Integer prefetch) {
      this.prefetch = prefetch;
    }

    /**
     * Gets the transaction size.
     *
     * @return the transaction size
     */
    public Integer getTransactionSize() {
      return this.transactionSize;
    }

    /**
     * Sets the transaction size.
     *
     * @param transactionSize the new transaction size
     */
    public void setTransactionSize(Integer transactionSize) {
      this.transactionSize = transactionSize;
    }

    /**
     * Gets the default requeue rejected.
     *
     * @return the default requeue rejected
     */
    public Boolean getDefaultRequeueRejected() {
      return this.defaultRequeueRejected;
    }

    /**
     * Sets the default requeue rejected.
     *
     * @param defaultRequeueRejected the new default requeue rejected
     */
    public void setDefaultRequeueRejected(Boolean defaultRequeueRejected) {
      this.defaultRequeueRejected = defaultRequeueRejected;
    }

    /**
     * Gets the retry.
     *
     * @return the retry
     */
    public ListenerRetry getRetry() {
      return this.retry;
    }

  }

  /**
   * The Class Template.
   */
  public static class Template {

    /** The retry. */
    // @NestedConfigurationProperty
    private final Retry retry = new Retry();

    /**
     * Enable mandatory messages. If a mandatory message cannot be routed to a queue by the server,
     * it will return an unroutable message with a Return method.
     */
    private Boolean mandatory;

    /**
     * Timeout for receive() operations.
     */
    private Long receiveTimeout;

    /**
     * Timeout for sendAndReceive() operations.
     */
    private Long replyTimeout;

    /**
     * Gets the retry.
     *
     * @return the retry
     */
    public Retry getRetry() {
      return this.retry;
    }

    /**
     * Gets the mandatory.
     *
     * @return the mandatory
     */
    public Boolean getMandatory() {
      return this.mandatory;
    }

    /**
     * Sets the mandatory.
     *
     * @param mandatory the new mandatory
     */
    public void setMandatory(Boolean mandatory) {
      this.mandatory = mandatory;
    }

    /**
     * Gets the receive timeout.
     *
     * @return the receive timeout
     */
    public Long getReceiveTimeout() {
      return this.receiveTimeout;
    }

    /**
     * Sets the receive timeout.
     *
     * @param receiveTimeout the new receive timeout
     */
    public void setReceiveTimeout(Long receiveTimeout) {
      this.receiveTimeout = receiveTimeout;
    }

    /**
     * Gets the reply timeout.
     *
     * @return the reply timeout
     */
    public Long getReplyTimeout() {
      return this.replyTimeout;
    }

    /**
     * Sets the reply timeout.
     *
     * @param replyTimeout the new reply timeout
     */
    public void setReplyTimeout(Long replyTimeout) {
      this.replyTimeout = replyTimeout;
    }

  }

  /**
   * The Class Retry.
   */
  public static class Retry {

    /**
     * Whether or not publishing retries are enabled.
     */
    private boolean enabled;

    /**
     * Maximum number of attempts to publish or deliver a message.
     */
    private int maxAttempts = 3;

    /**
     * Interval between the first and second attempt to publish or deliver a message.
     */
    private long initialInterval = 1000L;

    /**
     * A multiplier to apply to the previous retry interval.
     */
    private double multiplier = 1.0;

    /**
     * Maximum interval between attempts.
     */
    private long maxInterval = 10000L;

    /**
     * Checks if is enabled.
     *
     * @return true, if is enabled
     */
    public boolean isEnabled() {
      return this.enabled;
    }

    /**
     * Sets the enabled.
     *
     * @param enabled the new enabled
     */
    public void setEnabled(boolean enabled) {
      this.enabled = enabled;
    }

    /**
     * Gets the max attempts.
     *
     * @return the max attempts
     */
    public int getMaxAttempts() {
      return this.maxAttempts;
    }

    /**
     * Sets the max attempts.
     *
     * @param maxAttempts the new max attempts
     */
    public void setMaxAttempts(int maxAttempts) {
      this.maxAttempts = maxAttempts;
    }

    /**
     * Gets the initial interval.
     *
     * @return the initial interval
     */
    public long getInitialInterval() {
      return this.initialInterval;
    }

    /**
     * Sets the initial interval.
     *
     * @param initialInterval the new initial interval
     */
    public void setInitialInterval(long initialInterval) {
      this.initialInterval = initialInterval;
    }

    /**
     * Gets the multiplier.
     *
     * @return the multiplier
     */
    public double getMultiplier() {
      return this.multiplier;
    }

    /**
     * Sets the multiplier.
     *
     * @param multiplier the new multiplier
     */
    public void setMultiplier(double multiplier) {
      this.multiplier = multiplier;
    }

    /**
     * Gets the max interval.
     *
     * @return the max interval
     */
    public long getMaxInterval() {
      return this.maxInterval;
    }

    /**
     * Sets the max interval.
     *
     * @param maxInterval the new max interval
     */
    public void setMaxInterval(long maxInterval) {
      this.maxInterval = maxInterval;
    }

  }

  /**
   * The Class ListenerRetry.
   */
  public static class ListenerRetry extends Retry {

    /**
     * Whether or not retries are stateless or stateful.
     */
    private boolean stateless = true;

    /**
     * Checks if is stateless.
     *
     * @return true, if is stateless
     */
    public boolean isStateless() {
      return this.stateless;
    }

    /**
     * Sets the stateless.
     *
     * @param stateless the new stateless
     */
    public void setStateless(boolean stateless) {
      this.stateless = stateless;
    }

  }

}
